package com.vren.addressParse.modules.addressParse;

import com.alibaba.fastjson.JSONObject;
import com.vren.addressParse.modules.addressParse.domain.address.Address;
import com.vren.addressParse.modules.addressParse.domain.address.Format;
import com.vren.addressParse.modules.addressParse.domain.address.Result;
import com.vren.addressParse.modules.area.AreaService;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Description:
 * @Author: 张文Uncle
 * @Date: 2021-07-27 15:02
 **/
@Service
@Slf4j
public class AddressParseService {

    @Autowired
    private AreaService areaService;

    private String provinces;
    private String cities;
    private String areas;
    private String streets;

    private final static String EMPTY = "", BLANK = " ";

    private final static List<String> names = Arrays.asList("赵", "钱", "孙", "李", "周", "吴", "郑", "王", "冯", "陈", "楮", "卫", "蒋", "沈", "韩", "杨", "朱", "秦", "尤", "许", "何", "吕", "施", "张", "孔", "曹", "严", "华", "金", "魏", "陶", "姜", "戚", "谢", "邹", "喻", "柏", "水", "窦", "章", "云", "苏", "潘", "葛", "奚", "范", "彭", "郎", "鲁", "韦", "昌", "马", "苗", "凤", "花",
            "方", "俞", "任", "袁", "柳", "酆", "鲍", "史", "唐", "费", "廉", "岑", "薛", "雷", "贺", "倪", "汤", "滕", "殷", "罗", "毕", "郝", "邬", "安", "常", "乐", "于", "时", "傅", "皮", "卞", "齐", "康", "伍", "余", "元", "卜", "顾", "孟", "平", "黄", "和", "穆", "萧", "尹", "姚", "邵", "湛", "汪", "祁", "毛", "禹", "狄", "米", "贝",
            "明", "臧", "计", "伏", "成", "戴", "谈", "宋", "茅", "庞", "熊", "纪", "舒", "屈", "项", "祝", "董", "梁", "杜", "阮", "蓝", "闽", "席", "季", "麻", "强", "贾", "路", "娄", "危", "江", "童", "颜", "郭", "梅", "盛", "林", "刁", "锺", "徐", "丘", "骆", "高", "夏", "蔡", "田", "樊", "胡", "凌", "霍", "虞", "万", "支", "柯", "昝",
            "管", "卢", "莫", "经", "房", "裘", "缪", "干", "解", "应", "宗", "丁", "宣", "贲", "邓", "郁", "单", "杭", "洪", "包", "诸", "左", "石", "崔", "吉", "钮", "龚", "程", "嵇", "邢", "滑", "裴", "陆", "荣", "翁", "荀", "羊", "於", "惠", "甄", "麹", "家", "封", "芮", "羿", "储", "靳", "汲", "邴", "糜", "松", "井", "段", "富", "巫",
            "乌", "焦", "巴", "弓", "牧", "隗", "山", "谷", "车", "侯", "宓", "蓬", "全", "郗", "班", "仰", "秋", "仲", "伊", "宫", "宁", "仇", "栾", "暴", "甘", "斜", "厉", "戎", "祖", "武", "符", "刘", "景", "詹", "束", "龙", "叶", "幸", "司", "韶", "郜", "黎", "蓟", "薄", "印", "宿", "白", "怀", "蒲", "邰", "从", "鄂", "索", "咸", "籍",
            "赖", "卓", "蔺", "屠", "蒙", "池", "乔", "阴", "郁", "胥", "能", "苍", "双", "闻", "莘", "党", "翟", "谭", "贡", "劳", "逄", "姬", "申", "扶", "堵", "冉", "宰", "郦", "雍", "郤", "璩", "桑", "桂", "濮", "牛", "寿", "通", "边", "扈", "燕", "冀", "郏", "浦", "尚", "农", "温", "别", "庄", "晏", "柴", "瞿", "阎", "充", "慕", "连",
            "茹", "习", "宦", "艾", "鱼", "容", "向", "古", "易", "慎", "戈", "廖", "庾", "终", "暨", "居", "衡", "步", "都", "耿", "满", "弘", "匡", "国", "文", "寇", "广", "禄", "阙", "东", "欧", "殳", "沃", "利", "蔚", "越", "夔", "隆", "师", "巩", "厍", "聂", "晁", "勾", "敖", "融", "冷", "訾", "辛", "阚", "那", "简", "饶", "空", "曾",
            "毋", "沙", "乜", "养", "鞠", "须", "丰", "巢", "关", "蒯", "相", "查", "后", "荆", "红", "游", "竺", "权", "逑", "盖", "益", "桓", "公", "万俟", "司马", "上官", "欧阳", "夏侯", "诸葛", "闻人", "东方", "赫连", "皇甫", "尉迟", "公羊", "澹台", "公冶", "宗政", "濮阳", "淳于", "单于", "太叔", "申屠", "公孙", "仲孙", "轩辕", "令狐", "锺离", "宇文", "长孙",
            "慕容", "鲜于", "闾丘", "司徒", "司空", "丌官", "司寇", "仉", "督", "子车", "颛孙", "端木", "巫马", "公西", "漆雕", "乐正", "壤驷", "公良", "拓拔", "夹谷", "宰父", "谷梁", "晋", "楚", "阎", "法", "汝", "鄢", "涂", "钦", "段干", "百里", "东郭", "南门", "呼延", "归", "海", "羊舌", "微生", "岳", "帅", "缑", "亢", "况", "后", "有", "琴", "梁丘", "左丘",
            "东门", "西门", "商", "牟", "佘", "佴", "伯", "赏", "南宫", "墨", "哈", "谯", "笪", "年", "爱", "阳", "佟", "第五", "言", "福");

    private final static int nameMaxLength = 3;

    public Address parse(String address) {
        Result result = new Result();
        address = cleanAddress(address);
        address = parsePhone(address, result);
        List<String> splitAddress = Arrays.stream(address.split(BLANK)).filter(Strings::isNotBlank).map(String::trim).collect(Collectors.toList());
        for (String value : splitAddress) {
            if (result.getProvince().isEmpty() || result.getCity().isEmpty() || result.getArea().isEmpty() || result.getStreet().isEmpty()) {
                parseRegionWithRegexp(value, result);
            } else {
                result.getDetail().add(value);
            }
        }
        Address addressParse = new Address();
        String provinceName = result.getProvince().isEmpty() ? EMPTY : result.getProvince().get(0).getName();
        String cityName = result.getCity().isEmpty() ? EMPTY : result.getCity().get(0).getName();
        String areaName = result.getArea().isEmpty() ? EMPTY : result.getArea().get(0).getName();
        String streetName = result.getStreet().isEmpty() ? EMPTY : result.getStreet().get(0).getName();
        result.setDetail(result.getDetail().stream().map(item -> item.replace(provinceName, EMPTY)
                .replace(cityName, EMPTY)
                .replace(areaName, EMPTY)
                .replace(streetName, EMPTY)).collect(Collectors.toList()));
        List<String> detail = result.getDetail();
        detail = detail.stream().distinct().filter(item -> !EMPTY.equals(item)).collect(Collectors.toList());
        String name = EMPTY;
        for (String item : detail) {
            String resultName = parseName(item);
            int index = detail.indexOf(resultName);
            if (index > 0) {
                name = detail.get(index);
            } else if (detail.get(0).length() <= nameMaxLength && !Pattern.compile("[\u4e00-\u9fa5]").matcher(detail.get(0)).find()) {
                name = detail.get(0);
            } else {
                name = resultName;
            }
        }
        if (!EMPTY.equals(name)) {
            addressParse.setName(name);
            detail = detail.stream().distinct()
                    .filter(item -> !item.equals(addressParse.getName()))
                    .map(item -> item.replaceAll(addressParse.getName(), EMPTY))
                    .collect(Collectors.toList());
        }
        addressParse.setProvinceName(provinceName.split("，")[0]);
        addressParse.setProvinceCode(result.getProvince().isEmpty() ? 0 : result.getProvince().get(0).getCode());
        addressParse.setCityName(cityName.split("，")[0]);
        addressParse.setCityCode(result.getCity().isEmpty() ? 0 : result.getCity().get(0).getCode());
        List<String> streetList = Arrays.asList("市辖区", "区", "县", "镇");
        if (streetList.contains(addressParse.getCityName()) || "".equals(cityName)) {
            addressParse.setCityName(addressParse.getProvinceName());
        }
        addressParse.setPhone(result.getPhone());
        addressParse.setAreaName(areaName.split("，")[0]);
        addressParse.setAreaCode(result.getArea().isEmpty() ? 0 : result.getArea().get(0).getCode());
        addressParse.setStreetName(streetName.split("，")[0]);
        addressParse.setStreetCode(result.getStreet().isEmpty() ? 0 : result.getStreet().get(0).getCode());
        addressParse.setDetail(String.join(EMPTY, detail));
        return addressParse;
    }

    /**
     * 初始化结构数据
     */
    @PostConstruct
    private void init() {
        this.provinces = JSONObject.toJSONString(areaService.getProvinceList().stream().map((province) -> new Format(province.getCode(), String.format("%s，%s，%s", province.getName(), province.getErrorMapping(), province.getCustomMapping()), province.getProvince(), province.getCity(), province.getArea())).collect(Collectors.toList()));
        this.cities = JSONObject.toJSONString(areaService.getCityList().stream().map((province) -> new Format(province.getCode(), String.format("%s，%s，%s", province.getName(), province.getErrorMapping(), province.getCustomMapping()), province.getProvince(), province.getCity(), province.getArea())).collect(Collectors.toList()));
        this.areas = JSONObject.toJSONString(areaService.getAreaList().stream().map((province) -> new Format(province.getCode(), String.format("%s，%s，%s", province.getName(), province.getErrorMapping(), province.getCustomMapping()), province.getProvince(), province.getCity(), province.getArea())).collect(Collectors.toList()));
        this.streets = JSONObject.toJSONString(areaService.getStreetList().stream().map((province) -> new Format(province.getCode(), String.format("%s，%s，%s", province.getName(), province.getErrorMapping(), province.getCustomMapping()), province.getProvince(), province.getCity(), province.getArea())).collect(Collectors.toList()));
    }

    /**
     * 地址清洗
     */
    private String cleanAddress(String address) {
        address = address.replaceAll("\r\n", BLANK)
                .replaceAll("\r", " ").replaceAll("\n", " ").replaceAll("\t", BLANK);
        List<String> search = Arrays.asList("详细地址", "收货地址", "收件地址", "地址", "所在地区", "地区", "姓名", "收货人", "收件人",
                "联系人", "收", "邮编", "联系电话", "电话", "联系人手机号码", "手机号码", "手机号", "自治区直辖县级行政区划", "省直辖县级行政区划");
        for (String s : search) {
            address = address.replaceAll(s, BLANK);
        }
        return address.replaceAll("[`~!@#$^&*()=|{}':;',\\\\[\\\\].<>\\\\/?~！@#￥……&*（）——|{}【】‘；：”“’。，、？]", BLANK)
                .replaceAll("\\s{2,}", BLANK);
    }

    /**
     * 正则获取手机号
     */
    private String parsePhone(String address, Result result) {
        address = address.replaceAll("(\\\\d{3})-(\\\\d{4})-(\\\\d{4})", "$1$2$3")
                .replaceAll("(\\\\d{3}) (\\\\d{4}) (\\\\d{4})", "$1$2$3")
                .replaceAll("(\\\\d{4}) \\\\d{4} \\\\d{4}", "$1$2$3")
                .replaceAll("(\\\\d{4})", "$1$2$3");
        String phoneReg = "(\\d{7,12})|(\\d{3,4}-\\d{6,8})|(86-[1][0-9]{10})|(86[1][0-9]{10})|([1][0-9]{10})";
        String phone = EMPTY;
        Pattern pattern = Pattern.compile(phoneReg);
        Matcher matcher = pattern.matcher(address);
        if (matcher.find()) {
            phone = matcher.group();
            address = address.replace(phone, BLANK);
        }
        result.setPhone(phone);
        return address;
    }

    /**
     * 获取收件人姓名
     *
     * @param fragment
     * @return
     */
    private String parseName(String fragment) {
        if (fragment.isEmpty() || !Pattern.compile("[\u4e00-\u9fa5]").matcher(fragment).find()) {
            return EMPTY;
        }
        List<String> nameCall = Arrays.asList("先生", "小姐", "同志", "哥哥", "姐姐", "妹妹", "弟弟", "妈妈", "爸爸", "爷爷", "奶奶", "姑姑", "舅舅");
        if (nameCall.contains(fragment)) {
            return fragment;
        }
        List<String> filters = Arrays.asList("街道", "乡镇");
        if (filters.contains(fragment)) {
            return EMPTY;
        }
        String nameFirst = fragment.substring(0, 1);
        if (fragment.length() <= nameMaxLength && fragment.length() > 1 && names.contains(nameFirst)) {
            return fragment;
        }
        return EMPTY;
    }

    /**
     * 正则地址解析
     *
     * @param address 需要解析的地址
     */
    private void parseRegionWithRegexp(String address, Result result) {
        String matchString = EMPTY;
        Object provinceCode;
        Object cityCode;
        Object areaCode;
        if (result.getProvince().isEmpty()) {
            for (int i = 1; i < address.length(); i++) {
                String str = address.substring(0, i + 1);
                List<Format> res = getMatcherResult("[0-9]{1,6}", str, 0, 0, 0, provinces);
                if (res.isEmpty()) {
                    //直到匹配不到数据为止
                    break;
                }
                result.getProvince().clear();//以最后一个为主
                result.getProvince().add(res.get(0));
                matchString = str;
            }
            if (!result.getProvince().isEmpty()) {
                address = address.replaceAll(matchString, EMPTY);
            }
        }

        if (result.getCity().isEmpty()) {
            provinceCode = result.getProvince().isEmpty() ? "[0-9]{1,9}" : result.getProvince().get(0).getCode();
            for (int i = 1; i < address.length(); i++) {
                String str = address.substring(0, i + 1);
                List<Format> res = getMatcherResult("[0-9]{1,6}", str, provinceCode, 0, 0, cities);
                if (res.isEmpty()) {
                    break;
                }
                result.getCity().clear();
                result.getCity().add(res.get(0));
                matchString = str;
            }
            if (!result.getCity().isEmpty()) {
                //如果城市 不是空
                provinceCode = result.getProvince().isEmpty() ? result.getCity().get(0).getProvinceCode() : result.getProvince().get(0).getCode();
                address = address.replaceAll(matchString, EMPTY);
                if (result.getProvince().isEmpty()) {
                    List<Format> res = getMatcherResult(provinceCode, EMPTY, 0, 0, 0, provinces);
                    if (!res.isEmpty()) {
                        result.getProvince().add(res.get(0));
                    }
                }
            }
        }

        if (result.getArea().isEmpty()) {
            provinceCode = result.getProvince().isEmpty() ? "[0-9]{1,9}" : result.getProvince().get(0).getCode();
            cityCode = result.getCity().isEmpty() ? "[0-9]{1,9}" : result.getCity().get(0).getCode();
            for (int i = 1; i < address.length(); i++) {
                String str = address.substring(0, i + 1);
                List<Format> res = getMatcherResult("[0-9]{1,9}", str, provinceCode, cityCode, 0, areas);
                if (res.isEmpty()) {
                    break;
                }
                matchString = str;
                result.getArea().add(res.get(0));
            }
            if (!result.getArea().isEmpty()) {
                address = address.replaceAll(matchString, EMPTY);
            }
        }

        if (result.getStreet().isEmpty()) {
            provinceCode = result.getProvince().isEmpty() ? "[0-9]{1,9}" : result.getProvince().get(0).getCode();
            cityCode = result.getCity().isEmpty() ? "[0-9]{1,9}" : result.getCity().get(0).getCode();
            areaCode = result.getArea().size() != 1 ? "[0-9]{1,9}" : result.getArea().get(0).getCode();
            String street = getStreets(provinceCode, cityCode, areaCode);
            for (int i = 1; i < address.length(); i++) {
                String str = address.substring(0, i + 1);
                List<Format> res = getMatcherResult("[0-9]{1,9}", str, provinceCode, cityCode, areaCode, street);
                if (res.isEmpty()) {
                    break;
                }
                matchString = str;
                result.getStreet().clear();
                result.getStreet().add(res.get(0));
            }
            if (!result.getStreet().isEmpty()) {
                address = address.replaceAll(matchString, EMPTY);
                List<Long> areaCodes = result.getStreet().stream().map(Format::getAreaCode).collect(Collectors.toList());
                if (!result.getArea().isEmpty()) {
                    for (Format format : result.getArea()) {
                        if (areaCodes.contains(format.getCode())) {
                            result.getArea().clear();
                            result.getArea().add(format);
                            break;
                        }
                    }
                    provinceCode = result.getProvince().isEmpty() ? result.getArea().get(0).getProvinceCode() : result.getProvince().get(0).getCode();
                    cityCode = result.getCity().isEmpty() ? result.getArea().get(0).getCityCode() : result.getCity().get(0).getCode();
                    if (result.getProvince().isEmpty()) {
                        List<Format> res = getMatcherResult(provinceCode, EMPTY, 0, 0, 0, provinces);
                        if (!res.isEmpty()) {
                            result.getProvince().add(res.get(0));
                        }
                    }
                    if (result.getCity().isEmpty()) {
                        List<Format> res = getMatcherResult(cityCode, EMPTY, provinceCode, 0, 0, cities);
                        if (!res.isEmpty()) {
                            result.getCity().add(res.get(0));
                        }
                    }

                } else {
                    provinceCode = result.getProvince().isEmpty() ? result.getStreet().get(0).getProvinceCode() : result.getProvince().get(0).getCode();
                    cityCode = result.getCity().isEmpty() ? result.getStreet().get(0).getCityCode() : result.getCity().get(0).getCode();
                    areaCode = result.getArea().isEmpty() ? result.getStreet().get(0).getAreaCode() : result.getArea().get(0).getCode();
                    if (result.getProvince().isEmpty()) {
                        List<Format> res = getMatcherResult(provinceCode, EMPTY, 0, 0, 0, provinces);
                        if (!res.isEmpty()) {
                            result.getProvince().addAll(res);
                        }
                    }
                    if (result.getCity().isEmpty()) {
                        List<Format> res = getMatcherResult(cityCode, EMPTY, provinceCode, 0, 0, cities);
                        if (!res.isEmpty()) {
                            result.getCity().addAll(res);
                        }
                    }
                    if (result.getArea().isEmpty()) {
                        List<Format> res = getMatcherResult(areaCode, EMPTY, provinceCode, cityCode, 0, areas);
                        if (!res.isEmpty()) {
                            result.getArea().addAll(res);
                        }
                    }
                }

            }
        }
        if (address.length() > 0) {
            result.getDetail().add(address);
        }
    }

    private String getStreets(Object provinceCode, Object cityCode, Object areaCode) {
        if (areaCode instanceof Long) {
            return areaService.getStreetsByAreaCode((Long) areaCode);
        }
        if (cityCode instanceof Long) {
            return areaService.getStreetsByCityCode((Long) cityCode);
        }
        if (provinceCode instanceof Long) {
            return areaService.getStreetsByProvinceCode((Long) provinceCode);
        }
        return streets;
    }

    private List<Format> getMatcherResult(Object code, String name, Object provinceCode, Object cityCode, Object areaCode, String source) {
        List<Format> res = new ArrayList<>();
        String reg = String.format("\\{\"code\":%s,\"name\":\"*.%s[\\x{4e00}-\\x{9fa5}，null]*?\",\"provinceCode\":%s,\"cityCode\":%s,\"areaCode\":%s}",
                code, name, provinceCode, cityCode, areaCode);
        Pattern pattern = Pattern.compile(reg);
        Matcher matcher = pattern.matcher(source);
        while (matcher.find()) {
            res.add(JSONObject.parseObject(matcher.group(), Format.class));
        }
        return res;
    }

    public static void main(String[] args) {
        //广东省东莞市东城街道风岗镇翡翠山湖臻翠园三期5栋304 张文18715621823
    }
}
